package mastermind.view;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import mastermind.Client;
import mastermind.DataOperator;
import mastermind.game.Game;
import mastermind.game.GameException;
import mastermind.game.Pieces;
import mastermind.net.Protocol;
import mastermind.view.dialog.LoginDialog;
import mastermind.view.dialog.RegisterDialog;
/**
 * A GUI wrapper for the MM Client
 */
public class ClientGUI extends JFrame {
	private static final long serialVersionUID = -3423250710635477628L;
	private Client client;
	private MMBoard mmBoard;
	private JButton submit, newGame, login, register, logout, stats;
	private String currentUser;

	public ClientGUI() {
		setLayout(new BorderLayout());
		setSize(600, 400);

		// logout if user quits without explicitly pressing logout
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		addWindowListener(new ExitListener());

		mmBoard = new MMBoard(Protocol.NUM_GUESSES,
				Protocol.SECRET_DEFAULT_LENGTH);
		add(mmBoard, BorderLayout.CENTER);

		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new GridLayout(0, 1));

		submit = new JButton("submit");
		submit.setEnabled(false);
		submit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				Pieces p = mmBoard.getSelectedPieces();
				try {
					String feedback = client.guess(currentUser, p);
					if (feedback != null) {
						mmBoard.setPegs(feedback);
						if (Game.correctGuess(feedback)) {
							showWinMessage(mmBoard.getRow());
							submit.setEnabled(false);
							return;
						}
						mmBoard.nextRow();
					}
				} catch (GameException e) {
					errorMessage(e.getMessage(), "guess error");
					submit.setEnabled(false);
				}
			}
		});
		buttonPanel.add(submit);

		newGame = new JButton("new game");
		newGame.setEnabled(false);
		newGame.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				mmBoard.reset();
				getClient().startGame(currentUser);
				submit.setEnabled(true);
			}
		});
		buttonPanel.add(newGame);

		login = new JButton("login");
		login.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				try {
					client = new Client();
					new LoginDialog(ClientGUI.this);
				} catch (Exception e) {
					errorMessage("couldn't connect to server",
							"error connecting");
					return;
				}
			}
		});
		buttonPanel.add(login);

		register = new JButton("register");
		register.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				register();
			}
		});
		buttonPanel.add(register);

		logout = new JButton("logout");
		logout.setEnabled(false);
		logout.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if (client.logout(currentUser)) {
					setCurrentUser(null);
					setLoggedIn(false);
					submit.setEnabled(false);
					mmBoard.reset();
				} else {
					errorMessage("error logging out", "logout failed");
				}
			}
		});
		buttonPanel.add(logout);

		stats = new JButton("stats");
		stats.setEnabled(false);
		stats.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				double av = client.getStats(currentUser);
				Map<String, Double> lb = client.getLeaderBoard();
				StringBuilder sb = new StringBuilder();
				sb.append(String.format("Your average: %.2f\n\n", av));
				for (String username : lb.keySet()) {
					sb.append(String.format("%s: %.2f\n", username, lb
							.get(username)));
				}
				JOptionPane.showMessageDialog(ClientGUI.this, sb.toString(),
						"stats", JOptionPane.INFORMATION_MESSAGE);
			}
		});
		buttonPanel.add(stats);

		add(buttonPanel, BorderLayout.EAST);

		/*
		 * if this is the first time the client has been run, prompt for
		 * registration
		 */
		if (isFirstRun()) {
			register();
		}

		// center and display
		setLocationRelativeTo(null);
		setVisible(true);
	}

	/**
	 * @return if this is the first time the client has been run
	 */
	private boolean isFirstRun() {
		return !(new File(DataOperator.LOCAL_LOG).exists());
	}

	/**
	 * Create a new Client and show the registration dialog
	 */
	private void register() {
		try {
			client = new Client();
			new RegisterDialog(ClientGUI.this);
		} catch (Exception e) {
			errorMessage("couldn't connect to server", "error connecting");
			return;
		}
	}

	/**
	 * Set the current player of this client
	 * 
	 * @param currentUser
	 *            User name of the current player.
	 */
	public void setCurrentUser(String currentUser) {
		this.currentUser = currentUser;
	}

	/** Set buttons according to whether player is logged in */
	public void setLoggedIn(boolean loggedIn) {
		newGame.setEnabled(loggedIn);
		register.setEnabled(!loggedIn);
		login.setEnabled(!loggedIn);
		logout.setEnabled(loggedIn);
		stats.setEnabled(loggedIn);
	}

	/** Display an error message */
	public void errorMessage(String message, String title) {
		JOptionPane.showMessageDialog(this, message, title,
				JOptionPane.ERROR_MESSAGE);
	}

	/** Display a winning message according to number of guesses made */
	private void showWinMessage(int guesses) {
		String message = "winner";
		if (guesses <= 4) {
			message = "You are a genius";
		} else if (guesses <= 8) {
			message = "You are good";
		} else {
			message = "You are not bad";
		}

		JOptionPane.showMessageDialog(ClientGUI.this, message,
				"Congratulations", JOptionPane.INFORMATION_MESSAGE);
	}

	/**
	 * @return Current Client object
	 */
	public Client getClient() {
		return client;
	}
	
	/**
	 * Restores board state from saved guess/feedback
	 * @param guesses
	 */
	public void restoreGame(Map<Pieces, String> guesses) {
		mmBoard.restoreBoard(guesses);
		submit.setEnabled(true);
	}
	
	/** Logs player out if logged in when exiting */
	class ExitListener extends WindowAdapter {
		public void windowClosing(WindowEvent e) {
			// make sure player is logged in
			if (client != null && currentUser != null) {
				client.logout(currentUser);
			}
		}
	}
	
	class CancelListener implements ActionListener {
		private JDialog jd;

		public CancelListener(JDialog jd) {
			this.jd = jd;
		}

		public void actionPerformed(ActionEvent e) {
			jd.dispose();
		}
	}
	
	public static void main(String[] args) throws IOException {
		new ClientGUI();
	}

}
